package com.yupi.soulba.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.soulba.common.ErrorCode;
import com.yupi.soulba.exception.BusinessException;
import com.yupi.soulba.mapper.TeamMapper;
import com.yupi.soulba.model.domain.Team;
import com.yupi.soulba.model.domain.User;
import com.yupi.soulba.model.domain.UserTeam;
import com.yupi.soulba.model.dto.TeamQuery;
import com.yupi.soulba.model.enums.TeamStatusEnum;
import com.yupi.soulba.model.request.TeamDisbandRequest;
import com.yupi.soulba.model.request.TeamJoinRequest;
import com.yupi.soulba.model.request.TeamQuitRequest;
import com.yupi.soulba.model.request.TeamUpdateRequest;
import com.yupi.soulba.model.vo.TeamUserVo;
import com.yupi.soulba.model.vo.UserVo;
import com.yupi.soulba.service.TeamService;
import com.yupi.soulba.service.UserService;
import com.yupi.soulba.service.UserTeamService;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
* @author 过江辉
* @description 针对表【team(队伍)】的数据库操作Service实现
* @createDate 2024-12-15 11:18:32
*/
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team> implements TeamService{

    @Resource
    private UserTeamService userTeamService;
    
    @Resource
    private UserService userService;
    
    @Resource
    private RedissonClient redissonClient;
    
    @Resource
    private TeamMapper teamMapper;

    /**
     * 创建队伍
     * @param team
     * @param loginUser
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public long addTeam(Team team, User loginUser) {
        // 1. 请求参数是否为空
        if(team == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        // 2. 当前用户是否登录 未登录不允许创建队伍
        if(loginUser == null) {
            throw new BusinessException(ErrorCode.NOT_LOGIN);
        }
        long userId = loginUser.getId();
        // 3. 校验信息
        //    1. 队伍人数 > 1 且 <= 10
        int maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if (maxNum < 1 || maxNum > 10) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍人数不满足创建要求");
        }
        //    2. 队伍名长度 <= 20
        String name = team.getName();
        if(StringUtils.isBlank(name) || name.length() > 20){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍名不满足创建要求");
        }
        //    3. 队伍描述 <= 512
        String description = team.getDescription();
        if (StringUtils.isNotBlank(description) && description.length() > 512) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍描述过长,不满足创建要求");
        }
        //    4. status是否合法 不传默认为公开(0)
        int status = Optional.ofNullable(team.getStatus()).orElse(0);
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
        if(statusEnum == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不满足创建要求");
        }
        //    5. 如果队伍是加密状态 一定要有密码
        String passWord = team.getPassword();
        if(TeamStatusEnum.SECRET.equals(statusEnum) 
                && (StringUtils.isBlank(passWord) || passWord.length() > 32)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密队伍密码设置有误");
        }
        //    6. 过期时间 > 当前时间
        Date expireTime = team.getExpireTime();
        if(expireTime != null) {
            if(new Date().after(expireTime)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "过期时间早于当前时间");
            }
        }
        //    7. 用户最多创建或加入六个队伍
        //    todo 分布式锁 如果同时创建100个队伍？
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId", userId);
        long hasTeamNum = userTeamService.count(queryWrapper);
        if(hasTeamNum >= 6) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "最多创建和加入六个队伍");
        }
        // 4. 插入 队伍信息 => 队伍表
        team.setId(null);
        team.setUserId(userId);
        boolean result = this.save(team);
        Long teamId = team.getId();
        if(!result || teamId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }
        // 5. 插入 用户-队伍关系信息 => 用户队伍关系表
        UserTeam userTeam = new UserTeam();
        userTeam.setId(null);
        userTeam.setUserId(userId);
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(new Date());
        result = userTeamService.save(userTeam);
        Long userTeamId = userTeam.getId();
        if(!result || userTeamId == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "创建队伍失败");
        }
        return teamId;
    }

    /**
     * 解散队伍
     * @param teamDisbandRequest
     * @param loginUser
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean disbandTeam(TeamDisbandRequest teamDisbandRequest, User loginUser) {
        if(teamDisbandRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long disbandTeamId = teamDisbandRequest.getTeamId();
        if(disbandTeamId == null || disbandTeamId <= 0) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "解散队伍不存在");
        }
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", disbandTeamId);
        Team disbandTeam = this.getById(disbandTeamId);
        if(disbandTeam == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "解散队伍不存在");
        }
        // 只有管理员或队长有权解散队伍
        if(!(disbandTeam.getUserId().equals(loginUser.getId()) || userService.isAdmin(loginUser))) {
            throw new BusinessException(ErrorCode.NO_AUTH, "无权解散队伍");
        }
        // 删除用户队伍表关系数据
        this.removeUserTeamDataByTeamId(disbandTeamId);
        // 删除队伍
        QueryWrapper<Team> removeTeamQueryWrapper = new QueryWrapper<>();
        removeTeamQueryWrapper.eq("id", disbandTeamId);
        return this.remove(removeTeamQueryWrapper);
    }

    /**
     * 搜索队伍（公开 and 加密）
     * @param teamQuery
     * @return
     */
    @Override
    public List<TeamUserVo> listTeams(TeamQuery teamQuery, boolean isAdmin) {
        // 优先判断删除过期队伍
        QueryWrapper<Team> expireTeamQueryWrapper = new QueryWrapper<>();
        expireTeamQueryWrapper.and(q -> q.lt("expireTime", new Date()).or().isNull("expireTime"));
        List<Team> expireTeamList = this.list(expireTeamQueryWrapper);
        List<Long> expireTeamIdList = new ArrayList<>();
        for (Team team : expireTeamList) {
            expireTeamIdList.add(team.getId());
        }
        if(!expireTeamList.isEmpty()) {
            teamMapper.deleteBatchIds(expireTeamIdList);
        }
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        // 组合查询条件
        if (teamQuery != null) {
            Long id = teamQuery.getId();
            if(id != null && id > 0) {
                queryWrapper.eq("id", id);
            }
            List<Long> teamIdList = teamQuery.getIdList();
            if(CollectionUtils.isNotEmpty(teamIdList)) {
                queryWrapper.in("id", teamIdList);
            }
            // 搜索关键词（同时对 队伍名称 和 队伍描述 搜索）
            String searchKeyText = teamQuery.getSearchKeyText();
            if(StringUtils.isNotBlank(searchKeyText)) {
                queryWrapper.and(q -> q.like("name", searchKeyText).or().like("description", searchKeyText));
            }
            String name = teamQuery.getName();
            if(StringUtils.isNotBlank(name)) {
                queryWrapper.like("name", name);
            }
            String description = teamQuery.getDescription();
            if(StringUtils.isNotBlank(description)) {
                queryWrapper.like("description", description);
            }
            Integer maxNum = teamQuery.getMaxNum();
            // 查询最大人数相等的
            if(maxNum != null && maxNum > 0) {
                queryWrapper.eq("maxNum", maxNum);
            }
            Long userId = teamQuery.getUserId();
            // 根据创建人来查询
            if(userId != null && userId > 0) {
                queryWrapper.eq("userId", userId);
            }
            Integer status = teamQuery.getStatus();
            TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
            // 根据状态查询
            // 如果枚举值不存在 默认查询所有队伍
            if(statusEnum == null) {
                List<Integer> statusValueList = Arrays.asList(0, 1, 2);
                queryWrapper.in("status", statusValueList);
            } else {
                // 如果枚举值存在 按照前端要求查询的状态查询
                queryWrapper.eq("status", statusEnum.getValue());
            }
        }
//        // 不展示已过期的队伍
//        // expireTime = null or expireTime > now()
//        queryWrapper.and(q -> q.gt("expireTime", new Date()).or().isNull("expireTime"));
        List<Team> teamList = this.list(queryWrapper);
        if(CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }
// 1. 写 SQL 查询
// 查询队伍和创建人的信息: select * from team t left join user u on t.userId = u.id;
// 查询队伍和已加入队伍成员的信息: select * from team t join user_team ut on t.id = ut.teamId;
        // 关联查询创建人的信息
        List<TeamUserVo> teamUserVoList = new ArrayList<>();
        for (Team team : teamList) {
            Long userId = team.getUserId();
            if(userId == null) {
                continue;
            }
            User user = userService.getById(userId);
            TeamUserVo teamUserVo = new TeamUserVo();
            // 封装 teamUserVo
            BeanUtils.copyProperties(team, teamUserVo);
            //脱敏用户信息
            User safetyUser = userService.getSafetyUser(user);
            if(user != null) {
                UserVo userVo = new UserVo();
                // 封装 teamUserVo
                BeanUtils.copyProperties(user, userVo);
                // 将队伍创建人信息封装进 teamUserVo
                teamUserVo.setCreateUserVo(userVo);
            }
            teamUserVoList.add(teamUserVo);
        }
        return teamUserVoList;
    }

    /**
     * 搜索队伍（公开 and 加密）
     * @param searchKeyText
     * @return
     */
    @Override
    public List<TeamUserVo> listTeamsSearchTeam(String searchKeyText, boolean isAdmin) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        // 组合查询条件
        if (StringUtils.isNotBlank(searchKeyText)) {
            // 搜索关键词（同时对 队伍名称 和 队伍描述 搜索）
            if(StringUtils.isNotBlank(searchKeyText)) {
                queryWrapper.and(q -> q.like("name", searchKeyText).or().like("description", searchKeyText));
            }
            List<Integer> statusList = Arrays.asList(0, 2);
            queryWrapper.in("status", statusList);

        }
        // 不展示已过期的队伍
        // expireTime = null or expireTime > now()
        queryWrapper.and(q -> q.gt("expireTime", new Date()).or().isNull("expireTime"));
        List<Team> teamList = this.list(queryWrapper);
        if(CollectionUtils.isEmpty(teamList)) {
            return new ArrayList<>();
        }
// 1. 写 SQL 查询
// 查询队伍和创建人的信息: select * from team t left join user u on t.userId = u.id;
// 查询队伍和已加入队伍成员的信息: select * from team t join user_team ut on t.id = ut.teamId;
        // 关联查询创建人的信息
        List<TeamUserVo> teamUserVoList = new ArrayList<>();
        for (Team team : teamList) {
            Long userId = team.getUserId();
            if(userId == null) {
                continue;
            }
            User user = userService.getById(userId);
            TeamUserVo teamUserVo = new TeamUserVo();
            // 封装 teamUserVo
            BeanUtils.copyProperties(team, teamUserVo);
            //脱敏用户信息
            User safetyUser = userService.getSafetyUser(user);
            if(user != null) {
                UserVo userVo = new UserVo();
                // 封装 teamUserVo
                BeanUtils.copyProperties(user, userVo);
                // 将队伍创建人信息封装进 teamUserVo
                teamUserVo.setCreateUserVo(userVo);
            }
            teamUserVoList.add(teamUserVo);
        }
        return teamUserVoList;
    }
    
    /**
     * 更新队伍
     * @param teamUpdateRequest
     * @param loginUser
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User loginUser) {
        if(teamUpdateRequest == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long id = teamUpdateRequest.getId();
        if(id == null || id <= 0) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team oldTeam = this.getById(id);
        if(oldTeam == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "更新队伍不存在");
        }
        // 只允许管理员或队伍创建者修改队伍信息
        if(oldTeam.getUserId() != loginUser.getId() && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH, "无权更新队伍信息");
        }
        // 如果更新后的队伍状态为加密 并且旧队伍信息状态不是加密
        // （若不校验 会导致加密队伍无密码）则一定要设置密码
        TeamStatusEnum oldTeamStatus = TeamStatusEnum.getEnumByValue(oldTeam.getStatus());
        TeamStatusEnum newTeamStatus = TeamStatusEnum.getEnumByValue(teamUpdateRequest.getStatus());
        if (newTeamStatus.equals(TeamStatusEnum.SECRET) && !oldTeamStatus.equals(TeamStatusEnum.SECRET)) {
            // 判断密码是否为空
            if(StringUtils.isBlank(teamUpdateRequest.getPassword())) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "加密队伍必须有密码");
            }
        }
        Team newTeam = new Team();
        BeanUtils.copyProperties(teamUpdateRequest, newTeam);
        return this.updateById(newTeam);
    }

    /**
     * 加入队伍
     * @param teamJoinRequest
     * @param loginUser
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean joinTeam(TeamJoinRequest teamJoinRequest, User loginUser) {
        if(teamJoinRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long joinTeamId = teamJoinRequest.getTeamId();
        if(joinTeamId == null || joinTeamId <= 0) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "加入队伍不存在");
        }
        Team joinTeam = this.getById(joinTeamId);
        if(joinTeam == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "加入队伍不存在");
        }
        // 不能加入已过期的队伍
        Date expireTime = joinTeam.getExpireTime();
        if(expireTime != null && expireTime.before(new Date())) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "加入队伍已过期");
        }
        Integer status = joinTeam.getStatus();
        TeamStatusEnum joinTeamStatus = TeamStatusEnum.getEnumByValue(status);
        // 不能加入私有队伍
        if(joinTeamStatus.equals(TeamStatusEnum.PRIVATE)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "无法加入私有队伍");
        }
        // 加入加密队伍 需要密码验证通过
        String userPassword = teamJoinRequest.getPassword();
        String joinTeamPassword = joinTeam.getPassword();
        if(TeamStatusEnum.SECRET.equals(joinTeamStatus)) {
            if(StringUtils.isBlank(userPassword) || !userPassword.equals(joinTeamPassword)) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误");
            }
        }
        // 一个用户最多加入五个队伍
        // 加锁：既要锁队伍id（一个队伍不能被同时加入多次）
        //      又要锁用户id（一个用户不能被同时加入多个队伍）
        // 创建分布式锁
        RLock lock = redissonClient.getLock("soulBa:joinTeam:lock");
        long userId = loginUser.getId();
        try {
            // 只有一个线程能获取到锁 抢到锁后执行业务逻辑
            while(true){
                if(lock.tryLock(0, -1, TimeUnit.MILLISECONDS)){
//                    // 运行情况下注释掉 因为打印非常影响性能
//                    System.out.println("getLock: " + Thread.currentThread().getId());
                    QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("userId", userId);
                    long hasJoinTeamNum = userTeamService.count(queryWrapper);
                    if(hasJoinTeamNum >= 6){
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "最多创建或加入六个队伍");
                    }
                    // 不能加入已满队伍
                    long hasJoinUserNum = this.getTeamUserNumByTeamId(joinTeamId);
                    if(hasJoinUserNum >= joinTeam.getMaxNum()) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "加入队伍已满");
                    }
                    // 不能重复加入队伍
                    queryWrapper = new QueryWrapper<>();
                    queryWrapper.eq("teamId", joinTeamId);
                    queryWrapper.eq("userId", userId);
                    long flag = userTeamService.count(queryWrapper);
                    if(flag >= 1) {
                        throw new BusinessException(ErrorCode.PARAMS_ERROR, "已加入该队伍");
                    }
                    // 更新用户队伍关系数据
                    UserTeam userTeam = new UserTeam();
                    userTeam.setTeamId(joinTeamId);
                    userTeam.setUserId(userId);
                    userTeam.setJoinTime(new Date());
                    return userTeamService.save(userTeam);
                }
            }
        } catch (InterruptedException e) {
            log.error("joinTeam error", e);
            return false;
        } finally {
            /**
             * 这段代码一定要放进 finally ！！！
             */
            // 只能释放自己的锁
            if(lock.isHeldByCurrentThread()){
//                // 运行情况下注释掉 因为打印非常影响性能
//                System.out.println("unlock: " + Thread.currentThread().getId());
                lock.unlock();
            }
        }
    }

    /**
     * 退出队伍
     * @param teamQuitRequest
     * @param loginUser
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginUser) {
        if(teamQuitRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long quitTeamId = teamQuitRequest.getTeamId();
        if(quitTeamId == null || quitTeamId <= 0) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "退出队伍不存在");
        }
        Team quitTeam = this.getById(quitTeamId);
        if(quitTeam == null) {
            throw new BusinessException(ErrorCode.NULL_ERROR, "退出队伍不存在");
        }
        // 确认需要退出的用户在队伍中
        long userId = loginUser.getId();
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", quitTeamId);
        queryWrapper.eq("userId", userId);
        long count = userTeamService.count(queryWrapper);
        if(count < 1) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "退出失败 不在队伍中");
        }
        // 如果该队伍只剩一人（也就是队长） 则解散
        long hasJoinUserNum = this.getTeamUserNumByTeamId(quitTeamId);
        if(hasJoinUserNum == 1) {
            // 删除队伍和队伍与用户的关系数据
            this.removeUserTeamDataByTeamId(quitTeamId);
            // 删除队伍
            QueryWrapper<Team> removeTeamQueryWrapper = new QueryWrapper<>();
            removeTeamQueryWrapper.eq("id", quitTeamId);
            return this.remove(removeTeamQueryWrapper);
        }
        // 如果退出用户是队长(创建人) 将队长权限顺位给最早加入的队员
        if(quitTeam.getUserId() == userId) {
            QueryWrapper<UserTeam> transferPowerQueryWrapper = new QueryWrapper<>();
            transferPowerQueryWrapper.eq("teamId", quitTeamId);
            transferPowerQueryWrapper.last("order by id asc limit 2");
            List<UserTeam> list = userTeamService.list(transferPowerQueryWrapper);
            if(CollectionUtils.isEmpty(list) || list.size() != 2) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "权限顺位失败 系统错误");
            }
            UserTeam nextPowerUserTeam = list.get(1);
            Long nextPowerUserId = nextPowerUserTeam.getUserId();
            // 队长权限顺位
            Team updateTeam = new Team();
            updateTeam.setId(quitTeamId);
            updateTeam.setUserId(nextPowerUserId);
            boolean transferPowerResult = this.updateById(updateTeam);
            if(!transferPowerResult) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "权限顺位失败 系统错误");
            }
        }
        // 移除用户队伍表数据
        return userTeamService.remove(queryWrapper);
    }

    /**
     * 根据队伍Id获取队伍当前人数
     * @param teamId
     * @return
     */
    private long getTeamUserNumByTeamId (Long teamId){
        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId", teamId);
        return userTeamService.count(queryWrapper);
    }

    /**
     * 根据队伍Id删除用户队伍表数据
     *
     * @param teamId
     */
    private void removeUserTeamDataByTeamId (Long teamId) {
        QueryWrapper<UserTeam> removeUserTeamQueryWrapper = new QueryWrapper<>();
        removeUserTeamQueryWrapper.eq("teamId", teamId);
        userTeamService.remove(removeUserTeamQueryWrapper);
    }
}




